"""
Copyright 2009, Thomas Dejanovic, Jay Shurtz.
 
This is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.

This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this software; if not, write to the Free
Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA, or see the FSF site: http://www.fsf.org.

This is the most specific level of xml generation for register
hatchlings.  it contains information about how to generate and
write xml from the hatchling data structure.
"""

id = "$Id: hatch_xml.py 667 2010-07-01 00:12:17Z jayshurtz $"
# $URL: http://hatch.googlecode.com/svn/tags/taggle_release_2.3/hatch/hatch_targets/hatch_xml.py $
# $Author: jayshurtz $
version = " ".join(id.split()[1:3])


import xml.dom.minidom
from os import extsep
import to_hatch_structure
from hatch_log import *


def generate(hatchNode, moduleName):
    """ Turns hatch code into xml.
        Write out the complete xml module description required to
        implement the registers described in the hatch datastructure.

        'hatchNode' is the top node of the design.
        'moduleName' is the basename of the design file.

        A HatchNode has attributes, properties, and childNodes, which are XML'd 
        respectively as tag attributes, child-tags-with-values, and child-tags-with-attributes.  
        HatchNode properties do NOT themselves have properties, so a hatched XML 
        tag should *NEVER* have both attributes and values like this:
            <tag id='a'> value </tag>

        Attributes and inner tags are allowed however:
            <tag id='a'>
                <inner_tag> value </inner_tag>
                <inner_tag id='b'>
                    <clown> "bosco" </clown>
                </inner_tag>
            </tag>

        Nested node name expansions are currently handled in hatch_facade.py
    """
    logger.info(generate.func_name + "(" + hatchNodeClassAndName(hatchNode) + ", " + str(moduleName) + ")")
    doc = xml.dom.minidom.Document()    # Create the minidom document.
    generate_hatchling_xml(hatchNode, doc, doc) # Add hatchling to xml document (recursively), doc is the topmost parent.
    outputFile = open(moduleName + extsep + "xml", 'w')
    outputFile.write(doc.toprettyxml(indent=" "*4)) # Write to file.  
    outputFile.close()

def add_attribute(key, value, parent, doc=None):
    """ Add node as an xml attribute.
        'key' is the xml tag name.
        'value' is the xml tag value.
        'parent' is the parent xml tag.
        'doc' is ignored.
    """
    logger.debug("key, value: %s, %s" % (key, value))
    parent.setAttribute(str(key), repr(value))  # Put strings in quotes, others not.

def add_child(key, value, parent, doc):
    """ Add node as an xml tag.
        'key' is the xml tag name.
        'value' is the xml tag value.
        'parent' is the parent xml tag.
        'doc' is the xml document.
    """
    logger.debug("key, value: %s, %s" % (key, value))
    thisKey = doc.createElement(str(key))
    parent.appendChild(thisKey)
    thisValue = doc.createTextNode(repr(value)) # Put strings in quotes, others not.
    thisKey.appendChild(thisValue)

def generate_hatchling_xml(hatchling, parent, doc, nameCallback=add_attribute, propertiesCallback=add_child):
    """ Recurse through hatchling contents to get all HatchNode instances.
        Generate xml along the way by adding to the minidom document.
        'hatchling' is the current HatchNode, which must have already had it's .elaborate() method called.
        'parent' is the parent of the current hatchling (may be the 'doc' for top level hatchlings as doc also supports the appendChild method.)
        'doc' is the xml.dom.minidom instance.
        'callback' functions specify the callback function & format to use when adding hatchling properties to the xml.
    """
    thishatchling = doc.createElement(hatchling.__class__.__name__) # Create the hatchling xml tag.
    parent.appendChild(thishatchling)    # Add this hatchling to it's parent.
    for (key, value) in [('name', hatchling.name)]: # XXX hack 'name', 'parent' - encapsulate this using class attributes.
        nameCallback(key, value, thishatchling, doc)    # Add attributes in thishatchling.__dict__ : name, parent, etc.
    if False:   # Parent attribute is ignored when reading xml anyway, see hatch_xml_methods.py.
        parentName = None
        if hatchling.parent is not None:
            parentName = hatchling.parent.properties.get('unique_name', hatchling.parent.name)
        for (key, value) in [('parent', parentName)]: 
            nameCallback(key, value, thishatchling, doc)    # Add attributes in thishatchling.__dict__ : name, parent, etc.
    for (key, value) in hatchling.property_list():
        propertiesCallback(key, value, thishatchling, doc)  # Add this hatchling's properties to itself (stuff in hatchling.args)
    for node in hatchling.childNodes:  # Add any contents depth first.
        generate_hatchling_xml(node, thishatchling, doc, nameCallback, propertiesCallback)


# TODO - built-in-self-test 
if __name__ == "__main__":
    #import hatch_history
    #hatch_history.setupSimpleHistory(histfile=__file__.replace('.py','.pyhist'), length=500)  # Setup history for this file.
    consoleHandler.setLevel(logging.DEBUG)

    if False:
        from hatch_register import Register, Struct
        hatchBlock = Struct(name='timers', packed=False, target='small apb slave', input_ports="""tick""", prefix="test_a_", header=""" bla """)
        hatchBlock.Register('real_time_clock', width=32, sw='rw', comment="""This is the real time clock and is incremented at a rate of 32kHz""", \
            auto='i', auto_inc_on='tick', rollover=1, hw='r')
        hatchBlock.Struct("sub block",
            Struct("wer",   # elaborate should name this?
                Register('test_a', width=10, hw='s', default=0xa),
                Register('test_b', width=10, hw='c', default=0xb),
                Register('test_c', width=10, hw='i', default=0xc, sw='w'),
                hw='d', # all the registers in this pack should get hw=d
                packed=True),
            hw='w', # all the registers in this block should get hw=w
            # no sw action is specified but all these
            # registers should be readable.
            packed=True)
        bBlock = Struct("another_sub_block", 
            Register('test_d', width=10, hw='s', default=0xd),
            Register('test_e', width=10, hw='c', default=0xe),
            Register('test_f', width=10, hw='i', default=0xf),
            hw='c', # all the registers in this block should get hw=c
            sw='rw',
            packed=True)
        hatchBlock.add_content(bBlock)

        hatchling = hatchBlock
        hatchling.elaborate()
        generate(hatchling, "hatch_xml_test")
        print "Please check the 'hatch_xml_test.xml' file."


